﻿#include "app.h"
#include "app_anim.h"
#include "page/menu_bar/menu_bar.h"
#include "page/system/app_system.h"
#include "page/template/template.h"
#include "page/startup/app_startup.h"
#include "page/home/app_home.h"

static app_page_t* current_page;
static app_page_t* prev_page;
static uint8_t current_index;
static app_anim_state_t anim_state;

// HACK 1. 页面容器，每次添加页面时，需要手动加入，格式参考template
static app_page_t* app_pages[] = {
    &app_home_page,
    &app_startup_page,
    &app_template_page,
    &app_page_system
};

/**
 * @brief 状态机更新函数
 * @param page 
*/
static void app_state_update(app_page_t* page);

/**
 * @brief 判断是否需要缓存，当前版本不支持缓存，只支持覆盖
 * @param anim 
 * @return 
*/
bool is_over_anim(uint8_t anim)
{
    return (anim >= LOAD_ANIM_OVER_LEFT && anim <= LOAD_ANIM_OVER_BOTTOM);
}

/**
 * @brief 页面加载回调函数
 * @param page 
 * @return 
*/
static app_state_e state_load_execute(app_page_t* page) {
    app_log("page(%s, %d) state load", page->name, page->id);

    if (page->root != NULL)
    {
        app_log_error("page(%s, %d) root must be null", page->name, page->id);
    }

    // 创建ui根节点，后面调用unload时，会自动delete
    lv_obj_t* root_obj = lv_obj_create(lv_scr_act());
    lv_obj_remove_style_all(root_obj);
    lv_obj_set_size(root_obj, LV_HOR_RES, LV_VER_RES);
    //lv_obj_clear_flag(root_obj, LV_OBJ_FLAG_SCROLLABLE);
    lv_obj_set_style_bg_color(root_obj, lv_color_black(), 0);
    lv_obj_set_style_bg_opa(root_obj, LV_OPA_90, 0);
    lv_obj_set_user_data(root_obj, page);

    page->root = root_obj;
    page->ops->on_load(page);
    // 可加入页面更新代码
    page->ops->on_did_load(page);

    return PAGE_STATE_WILL_APPEAR;
}

/**
 * @brief 动画运行完成函数
 * @param a 
*/
static void switch_anim_finish(lv_anim_t* a) {
    app_page_t* page = (app_page_t*)lv_anim_get_user_data(a);

    app_log("page(%s) anim finish", page->name);

    app_state_update(page);
    anim_state.is_busy = false;
    anim_state.is_switch_req = false;

    prev_page = current_page;
}

/**
 * @brief 创建页面切换动画
 * @param page 
*/
void switch_anim_create(app_page_t* page)
{
    app_anim_attr_t animAttr;
    lv_anim_t a;
    app_anim_init(&a, &animAttr);
    lv_anim_set_user_data(&a, page);
    lv_anim_set_var(&a, page->root);
    lv_anim_set_ready_cb(&a, switch_anim_finish);
    lv_anim_set_exec_cb(&a, animAttr.setter);

    int32_t start = 0;

    if (animAttr.getter)
    {
        start = animAttr.getter(page->root);
    }

    if (anim_state.is_pushing)
    {
        if (page->anim.is_enter)
        {
            lv_anim_set_values(
                &a,
                animAttr.push.enter.start,
                animAttr.push.enter.end
            );
        }
        else /* Exit */
        {
            lv_anim_set_values(
                &a,
                start,
                animAttr.push.exit.end
            );
        }
    }
    else /* Pop */
    {
        if (page->anim.is_enter)
        {
            lv_anim_set_values(
                &a,
                animAttr.pop.enter.start,
                animAttr.pop.enter.end
            );
        }
        else /* Exit */
        {
            lv_anim_set_values(
                &a,
                start,
                animAttr.pop.exit.end
            );
        }
    }

    lv_anim_start(&a);
    page->anim.is_busy = true;
}

static app_state_e state_will_appear_execute(app_page_t* page)
{
    app_log("page(%s, %d) state will appear", page->name, page->id);
    page->ops->on_will_appear(page);
    lv_obj_clear_flag(page->root, LV_OBJ_FLAG_HIDDEN);
    switch_anim_create(page);
    return PAGE_STATE_DID_APPEAR;
}

static app_state_e state_did_appear_execute(app_page_t* page)
{
    app_log("page(%s, %d) state did appear", page->name, page->id);
    page->ops->on_did_appear(page);
    return PAGE_STATE_ACTIVITY;
}

static app_state_e state_will_disappear_execute(app_page_t* page)
{
    app_log("page(%s, %d) state will disappear", page->name, page->id);
    page->ops->on_will_disappear(page);
    switch_anim_create(page);
    return PAGE_STATE_DID_DISAPPEAR;
}

static app_state_e state_did_disappear_execute(app_page_t* page)
{
    app_log("page(%s, %d) state did disappear", page->name, page->id);
    if (app_anim_get_type() == LOAD_ANIM_FADE_ON)
    {
        app_log("anim_state.type == LOAD_ANIM_FADE_ON, Page(%s) hidden", page->name);
        lv_obj_add_flag(page->root, LV_OBJ_FLAG_HIDDEN);
    }
    page->ops->on_did_disappear(page);

    return PAGE_STATE_UNLOAD;
}

static app_state_e state_unload_execute(app_page_t* page)
{
    app_log("page(%s) state unload", page->name);
    if (page->root == NULL)
    {
        app_log_warn("Page is loaded!");
        goto Exit;
    }

    lv_obj_del_async(page->root);
    page->root = NULL;
    page->ops->on_did_unload(page);

Exit:
    return PAGE_STATE_IDLE;
}

static void app_state_update(app_page_t* page) {
    if (page == NULL)
        return;

    switch (page->state)
    {
    case PAGE_STATE_IDLE:
        break;

    case PAGE_STATE_LOAD:
        page->state = state_load_execute(page);
        app_state_update(page);
        break;

    case PAGE_STATE_WILL_APPEAR:
        page->state = state_will_appear_execute(page);
        break;

    case PAGE_STATE_DID_APPEAR:
        page->state = state_did_appear_execute(page);
        app_log("page(%s, %d) state active", page->name, page->id);
        break;

    case PAGE_STATE_ACTIVITY:
        app_log("page(%s, %d) state active break", page->name, page->id);
        page->state = PAGE_STATE_WILL_DISAPPEAR;
        app_state_update(page);
        break;

    case PAGE_STATE_WILL_DISAPPEAR:
        page->state = state_will_disappear_execute(page);
        break;

    case PAGE_STATE_DID_DISAPPEAR:
        page->state = state_did_disappear_execute(page);
        if (page->state == PAGE_STATE_UNLOAD)
        {
            app_state_update(page);
        }
        break;

    case PAGE_STATE_UNLOAD:
        page->state = state_unload_execute(page);
        break;

    default:
        app_log_error("page(%s) state[%d] was NOT FOUND!", page->name, page->state);
        break;
    }
}

void app_init()
{
    // 初始化管理器本身属性
    current_index = UINT8_MAX;
    current_page = NULL;
    prev_page = NULL;
    anim_state.is_busy = false;
    anim_state.is_switch_req = false;
    anim_state.is_pushing = true;

    // 初始化主题
    lv_disp_t* display = lv_disp_get_default();
    lv_theme_t* th = lv_theme_default_init(display,  /*Use the DPI, size, etc from this display*/
        lv_color_hex(0x524C9C), lv_color_hex(0xDBDBDB),   /*Primary and secondary palette*/
        false,    /*Light or dark mode*/
        &lv_font_montserrat_10, &lv_font_montserrat_14, &lv_font_montserrat_18); /*Small, normal, large fonts*/

    lv_disp_set_theme(display, th); /*Assign the theme to the display*/

    // 初始化左侧菜单
    menu_bar_init();
}

static bool switch_anim_state_check() {
    if (anim_state.is_busy || anim_state.is_switch_req) {
        return false;
    }

    return true;
}

void app_switch_by_page(app_page_t* page) {
    if (page == NULL) {
        return;
    }

    if (anim_state.is_switch_req) {
        return;
    }

    anim_state.is_switch_req = true;
    current_page = page;
    current_page->state = PAGE_STATE_LOAD;

    if (prev_page != NULL) {
        prev_page->anim.is_enter = false;
    }
    current_page->anim.is_enter = true;

    anim_state.is_pushing = true;

    // 更新上个页面状态
    app_state_update(prev_page);
    // 更新当前页面状态
    app_state_update(current_page);

    if (anim_state.is_pushing)
    {
        if (prev_page)lv_obj_move_foreground(prev_page->root);
        lv_obj_move_foreground(current_page->root);
    }
    else
    {
        lv_obj_move_foreground(current_page->root);
        if (prev_page)lv_obj_move_foreground(prev_page->root);
    }
}

void app_switch_by_index(uint8_t index)
{
    uint8_t app_page_count = sizeof(app_pages) / sizeof(app_pages[0]);
    // 检测当前动画是否可用
    if (!switch_anim_state_check()) {
        return;
    }

    // 检测是否导航到自己
    if (index == current_index) {
        return;
    }

    // 检测该页面是否存在
    if (index >= app_page_count) {
        return;
    }
    // 导航到页面
    app_switch_by_page(app_pages[index]);
}

void app_switch_by_name(const char* name) {
    uint8_t app_page_count = sizeof(app_pages) / sizeof(app_pages[0]);

    if (name == NULL) {
        return;
    }

    for (uint8_t i = 0; i < app_page_count; i++) {
        if (app_pages[i] == NULL) {
            continue;
        }
        if (strcmp(name, app_pages[i]->name) == 0) {
            app_switch_by_page(app_pages[i]);
            return;
        }
    }
}
